""" System imports """
import os, sys
import logging

""" Other modules """
import pygame

""" Game """
from constants import *
import resourcemanager
import inputhandler
import player
import fov
from npc.npc import NPC
from eventrouter import eventpanel
from npc.shopkeeper import Shopkeeper
import session


""" Constants """
LEVEL_FOLDER = "levels"

""" Set up logging """
_log = logging.getLogger("epicquest.level")
_log.debug("Initializing Level")

class TileType():
    
    collide = False
    damage = False
    kill = False
    solid = False
    
    def __init__(self, image=None):
        self.image = image
        
    def set_image(self, image):
        self.image = resourcemanager.ResourceManager().load_image(image)
        
    def get_surface(self, tilefont):
        if self.image is None:
            tile_surf = tilefont.render("?", True, (0, 255, 0))
            tile = pygame.surface.Surface((TILE_WIDTH, TILE_HEIGHT))
            tile.blit(tile_surf, (0, 0))
            self.image = tile
        return self.image
        
class FloorTile(TileType):
    
    def get_surface(self, tilefont):
        if self.image is None:
            tile = pygame.surface.Surface((TILE_WIDTH, TILE_HEIGHT))
            tile.fill((0, 0, 0))
            self.image = tile
        return self.image
        
class WallTile(TileType):

    collide = True
    solid = True
    
    def get_surface(self, tilefont):
        if self.image is None:
            tile_surf = tilefont.render("#", True, (255, 255, 255))
            tile = pygame.surface.Surface((TILE_WIDTH, TILE_HEIGHT))
            tile.blit(tile_surf, (0, 0))
            self.image = tile
        return self.image
        
class SecretWallTile(TileType):

    solid = True
    def get_surface(self, tilefont):
        if self.image is None:
            tile_surf = tilefont.render("#", True, (205, 205, 205))
            tile = pygame.surface.Surface((TILE_WIDTH, TILE_HEIGHT))
            tile.blit(tile_surf, (0, 0))
            self.image = tile
        return self.image

        
class DangerousTile(TileType):
    
    damage = True
    
    def get_surface(self, tilefont):
        if self.image is None:
            tile_surf = tilefont.render("!", True, (255, 255, 255))
            tile = pygame.surface.Surface((TILE_WIDTH, TILE_HEIGHT))
            tile.blit(tile_surf, (0, 0))
            self.image = tile
        return self.image

        
class PitTile(TileType):

    kill = True

    def get_surface(self, tilefont):
        if self.image is None:
            tile_surf = tilefont.render("O", True, (255, 255, 255))
            tile = pygame.surface.Surface((TILE_WIDTH, TILE_HEIGHT))
            tile.blit(tile_surf, (0, 0))
            self.image = tile
        return self.image

class Tile(pygame.sprite.DirtySprite):
    """ Class to manage all the attributes of a tile
        
        Tile types:
        1 - Floor
        2 - Door
        3 - Wall
        4 - Danger
        5 - Pit
        6 - Secret Tile
        
    """
    
    collide = False
    type = TileType();
       
    def __init__(self, type=TileType(), cust_coords=None):
        pygame.sprite.DirtySprite.__init__(self)
        self.type = type
        self.collide = self.type.collide
        self.damage = self.type.damage
        self.kill = self.type.kill
        self.coords = cust_coords
        self.tilefont = pygame.font.SysFont("Courier New", 22)
        self.tile_image = self.type.get_surface(self.tilefont)
        self.hidden_image = pygame.surface.Surface((TILE_WIDTH, TILE_HEIGHT))
        self.hidden_image.fill((10, 10, 10))
        self.rect = pygame.rect.Rect((TILE_WIDTH * self.coords[0], TILE_HEIGHT * self.coords[1]), (TILE_WIDTH, TILE_HEIGHT))
        self.dirty = 1
        self.layer = 0
        self.visible = 1
        self.discovered = 0
        self.image = self.hidden_image
        
    def update(self, view_rect):
        if (self.rect.colliderect(view_rect)):
            self.dirty = 1
        #self.visible = 1
        #self.visible = self.discovered
        if (self.discovered == 1):
            self.image = self.tile_image
        #self.dirty = 0
    
    def draw(self, surface):
        surf = self.type.get_surface(self.tilefont)
        surface.blit(surf, self.rect.topleft)

class Level():
    
    tilefont = None
    size = [0, 0]
    tilegroup = pygame.sprite.LayeredDirty()
    
    def __init__(self, newsession=None, file=None):
        if file is not None:
            self.load_level(os.path.join(LEVEL_FOLDER, file))
        self.tilefont = pygame.font.SysFont("Courier New", 22)
        self.session = newsession
        if self.session == None:
            self.session = session.Session()
            
        self.session.state['coords'] = [1, 0]
        self.player = player.Player(self.session.state['coords'])
        self.tiles = []
        self.npcs = pygame.sprite.LayeredDirty()
        self.background = pygame.surface.Surface(SCREEN_SIZE)
        self.background.fill((0, 0, 0))
        
        """ Events """
#        eventpanel.subscribe(self, eventpanel.PLAYER_MOVE, )      
    
    def load_level(self, filename=None):
        file = open(filename, "r")
        self.size[0] = int(file.readline())
        self.size[1] = int(file.readline())
        self.tiles = [[0 for col in range(self.size[1])] for row in range(self.size[0])]
        try:
            xcol = 0
            yrow = 0
            for line in file:
                new_row = []
                xcol = 0
                while xcol < self.size[0]:
                    #print line[x]
                    type = int(line[xcol])
                    if type == 1:
                        tile = Tile(FloorTile(), (xcol, yrow))
                    elif type == 2:
                        tile = Tile(FloorTile(), (xcol, yrow))
                    elif type == 3:
                        tile = Tile(WallTile(), (xcol, yrow))
                    elif type == 4:
                        tile = Tile(DangerousTile(), (xcol, yrow))
                    elif type == 5:
                        tile = Tile(PitTile(), (xcol, yrow))
                    elif type == 6:
                        tile = Tile(SecretWallTile(), (xcol, yrow))
                    self.tilegroup.add(tile)
                    self.tiles[xcol][yrow] = tile
                    xcol = xcol + 1
                yrow = yrow + 1
                #self.size = (xcol, yrow)
        finally:
            file.close()
            self.npcs.add(NPC(coords=(5, 5)))
            self.npcs.add(Shopkeeper(coords=(12,1)))
    
    
    def draw(self, surface=None):
            
        world_surf = pygame.surface.Surface((self.size[0] * TILE_WIDTH, self.size[1] * TILE_HEIGHT))
       
        self.tilegroup.clear(world_surf, self.background)
        self.npcs.clear(world_surf, self.background)
        
        self.do_lighting()
        
        self.tilegroup.draw(world_surf)
        self.npcs.draw(world_surf)
        
        #pygame.draw.rect(world_surf,(200,0,0),self.player.view_rect,1)
        self.player.draw(world_surf)
        
        # Finally, draw only the relevant sections
        surface.blit(world_surf, (0, 0), self.player.view_rect)
        
    def isTileSolid(self, x, y):
        if self.tiles[x][y].type.solid:
            return True
        return False
    
    def visitTile(self, x, y):
        self.tiles[x][y].visible = 1
        self.tiles[x][y].dirty = 1
        self.tiles[x][y].discovered = 1
        
    def do_lighting(self):
        fov.fieldOfView(self.player.coords[0], self.player.coords[1], self.size[0], self.size[1], self.player.view_dist, self.visitTile, self.isTileSolid)    
        
    def dump_level(self):
        dump = ""
        for x, row in enumerate(self.tiles):
            for y, tile in enumerate(row):
                if row[y].collide:
                    symbol = "X"
                else:
                    symbol = " "
                dump = dump + symbol
        print dump
    
    def update(self):
        offset = (0, 0)
        if inputhandler.is_pressed(UP):
            offset = (offset[0], -1)
        if inputhandler.is_pressed(DOWN):
            offset = (offset[0], 1)
        if inputhandler.is_pressed(LEFT):
            offset = (-1, offset[1])
        if inputhandler.is_pressed(RIGHT):
            offset = (1, offset[1])
        
        loc = self.player.coords
        loc = (loc[0] + offset[0], loc[1] + offset[1])
        if self.valid_destination(loc):
            self.player.move(loc)
        
        #for y in self.tiles:
            #for tile in y:
                #tile.update()

        self.tilegroup.update(self.player.view_rect)
        self.npcs.update(self.player.view_rect)
    
    def valid_destination(self, loc):
        if loc[0] < 0 or loc[0] >= self.size[0]:
            return False
        if loc[1] < 0 or loc[1] >= self.size[1]:
            return False
            
        if self.tiles[loc[0]][loc[1]].type.collide == True:
            return False
        else:
            return True

